#include <iostream>
#include <cmath>
#include "mpreal.h"
#include "SLI.h"



///////////////////////////////////////////////////////////////////////////////////////////////////
//  Torre de numeros  /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

class Real
  {
    protected:
      virtual void printOn  (std::ostream &) const throw () = 0;
      virtual void readFrom (std::istream &)       throw () = 0;

    public:
      /* constructores / asignacion / destructores */
      explicit Real (double = 0.0) throw () { return; }
               Real (const Real &) throw () { return; }

      virtual Real & operator = (const Real &) throw () { return * this; }

      virtual ~Real () throw () { return; }

      /* constructores virtuales */
      virtual Real * create (double = 0.0) const throw () = 0;
      virtual Real * clone  (void)         const throw () = 0;

      /* funciones basicas */
      virtual Real & add (const Real & _r) throw () { return this->sub (_r.clone ()->neg ()); }
      virtual Real & sub (const Real & _r) throw () { return this->add (_r.clone ()->neg ()); }
      virtual Real & mul (const Real & _r) throw () { return this->div (_r.clone ()->inv ()); }
      virtual Real & div (const Real & _r) throw () { return this->mul (_r.clone ()->inv ()); }
      virtual Real & pow (const Real & _r) throw () { return this->roo (_r.clone ()->inv ()); }
      virtual Real & roo (const Real & _r) throw () { return this->pow (_r.clone ()->inv ()); }

      /* funciones derivadas */
      virtual Real & sum (Real const * _p) throw () { (* this) = * this->create (0.0L); while (_p) this->add (* _p++); return * this; }
      virtual Real & pro (Real const * _p) throw () { (* this) = * this->create (1.0L); while (_p) this->mul (* _p++); return * this; }
      virtual Real & neg (void) throw () { return (* this) = this->create (0.0L)->sub (* this); }
      virtual Real & inv (void) throw () { return (* this) = this->create (1.0L)->div (* this); }
      virtual Real & sqr (void) throw () { return this->roo (* this->create (2.0L)); }

      /* input / output */
      friend std::ostream & operator << (std::ostream & _o, Real const & _r) { _r.printOn  (_o); return _o; }
      friend std::istream & operator >> (std::istream & _i, Real       & _r) { _r.readFrom (_i); return _i; }
  };


class LongDouble : public Real
  {
    private:
      // Estructura para descomponer un long double (NB: REQUIERE gcc)
      union LD
        {
          long double d;
          struct
            {
              unsigned long long m : 64;
              unsigned short     e : 15;
              bool               s :  1;
            } __attribute__ ((aligned (1), packed));
        }     __attribute__ ((aligned (1), packed)) l;

      // Limitacion de precision (LongDouble_prec bits)
      LD           _limit (LD          d) const throw () { if (LongDouble_prec > 64)  LongDouble_prec = 64; d.m &= (64 - LongDouble_prec) < 64 ? 0xFFFFFFFFFFFFFFFFULL << (64 - LongDouble_prec) : 0L; return d; }
      long double  _limit (long double d) const throw () { return _limit (LD ({d})).d; }
      LongDouble & _limit (void         )       throw () { l = _limit (l); return * this; }

    protected:
      virtual void printOn  (std::ostream & _o) const throw () {                        _o << l.d;                      }
      virtual void readFrom (std::istream & _i)       throw () { long double _d = 0.0L; _i >>  _d; l.d = _d; _limit (); }

    public:
      /* Precision del LongDouble en bits (entre 0 y 64 inclusive) */
      static unsigned short LongDouble_prec;

      /* constructores / asignacion / destructores */
      explicit LongDouble (double _d = 0.0)       throw () :              l ({_limit (_d)}) { return; }
               LongDouble (const LongDouble & _l) throw () : Real ( _l ), l (         _l.l) { return; }

      virtual LongDouble & operator = (const LongDouble & _l) throw () { if (this != & _l) l = _l.l; return _limit (); }

      virtual ~LongDouble () throw () { return; }

      /* constructores virtuales */
      virtual LongDouble * create (double _d = 0.0) const throw () { return new LongDouble (_d);     }
      virtual LongDouble * clone  (void)            const throw () { return new LongDouble (* this); }

      /* funciones basicas */
      virtual LongDouble & add (const LongDouble & _r) throw () { l.d += _r.l.d; return _limit (); }
      virtual LongDouble & sub (const LongDouble & _r) throw () { l.d -= _r.l.d; return _limit (); }
      virtual LongDouble & mul (const LongDouble & _r) throw () { l.d *= _r.l.d; return _limit (); }
      virtual LongDouble & div (const LongDouble & _r) throw () { l.d /= _r.l.d; return _limit (); }
      virtual LongDouble & pow (const LongDouble & _r) throw () { l.d = std::pow (l.d, _r.l.d                 ); return _limit (); }
      virtual LongDouble & roo (const LongDouble & _r) throw () { l.d = std::pow (l.d, _r.clone ()->inv ().l.d); return _limit (); }

      /* funciones derivadas */
      virtual LongDouble & sum (LongDouble const * _p) throw () { l.d = 0.0L; while (_p) l.d += _p++->l.d; return _limit (); }
      virtual LongDouble & pro (LongDouble const * _p) throw () { l.d = 1.0L; while (_p) l.d *= _p++->l.d; return _limit (); }
      virtual LongDouble & neg (void) throw () { l.d =       -l.d; return _limit (); }
      virtual LongDouble & inv (void) throw () { l.d = 1.0L / l.d; return _limit (); }
      virtual LongDouble & sqr (void) throw () { l.d = std::sqrt (l.d); return _limit (); }
  };

unsigned short LongDouble::LongDouble_prec = 64;


class MPFR : public Real
  {
    private:
      mpfr::mpreal m;

    protected:
      virtual void printOn  (std::ostream & _o) const throw () { _o << m; }
      virtual void readFrom (std::istream & _i)       throw () { _i >> m; }

    public:
      /* constructores / asignacion / destructores */
      explicit MPFR (double _d = 0.0) throw () :    m (_d)           { return; }
               MPFR (const MPFR & _l) throw () : Real (_l), m (_l.m) { return; }

      virtual MPFR & operator = (const MPFR & _l) throw () { if (this != & _l) m = _l.m; return * this; }

      virtual ~MPFR () throw () { return; }

      /* constructores virtuales */
      virtual MPFR * create (double _d = 0.0) const throw () { return new MPFR (_d);     }
      virtual MPFR * clone  (void)            const throw () { return new MPFR (* this); }

      /* funciones basicas */
      virtual MPFR & add (const MPFR & _r) throw () { m += _r.m; return * this; }
      virtual MPFR & sub (const MPFR & _r) throw () { m -= _r.m; return * this; }
      virtual MPFR & mul (const MPFR & _r) throw () { m *= _r.m; return * this; }
      virtual MPFR & div (const MPFR & _r) throw () { m /= _r.m; return * this; }
      virtual MPFR & pow (const MPFR & _r) throw () { m = mpfr::pow (m, _r.m                 ); return * this; }
      virtual MPFR & roo (const MPFR & _r) throw () { m = mpfr::pow (m, _r.clone ()->inv ().m); return * this; }

      /* funciones derivadas */
      virtual MPFR & sum (MPFR const * _p) throw () { m = 0.0L; while (_p) m += _p++->m; return * this; }
      virtual MPFR & pro (MPFR const * _p) throw () { m = 1.0L; while (_p) m *= _p++->m; return * this; }
      virtual MPFR & neg (void) throw () { m =       -m; return * this; }
      virtual MPFR & inv (void) throw () { m = 1.0L / m; return * this; }
      virtual MPFR & sqr (void) throw () { m = mpfr::sqrt (m); return * this; }
  };


SLI SLIpow (const SLI & p, const SLI & q) { return pow (p, q); }

class SLIReal : public Real
  {
    private:
      SLI s;

    protected:
      virtual void printOn  (std::ostream & _o) const throw () { _o << s; }
      virtual void readFrom (std::istream & _i)       throw () { double d; _i >> d; s = d; }

    public:
      /* constructores / asignacion / destructores */
      explicit SLIReal (double _d = 0.0)     throw () :    s (_d)           { return; }
               SLIReal (const SLIReal & _l)  throw () : Real (_l), s (_l.s) { return; }

      virtual SLIReal & operator = (const SLIReal & _l) throw () { if (this != & _l) s = _l.s; return * this; }

      virtual ~SLIReal () throw () { return; }

      /* constructores virtuales */
      virtual SLIReal * create (double _d = 0.0) const throw () { return new SLIReal (_d);     }
      virtual SLIReal * clone  (void)            const throw () { return new SLIReal (* this); }

      /* funciones basicas */
      virtual SLIReal & add (const SLIReal & _r) throw () { s += _r.s; return * this; }
      virtual SLIReal & sub (const SLIReal & _r) throw () { s -= _r.s; return * this; }
      virtual SLIReal & mul (const SLIReal & _r) throw () { s *= _r.s; return * this; }
      virtual SLIReal & div (const SLIReal & _r) throw () { s /= _r.s; return * this; }
      virtual SLIReal & pow (const SLIReal & _r) throw () { s = SLIpow (s, _r.s                 ); return * this; }
      virtual SLIReal & roo (const SLIReal & _r) throw () { s = SLIpow (s, _r.clone ()->inv ().s); return * this; }

      /* funciones derivadas */
      virtual SLIReal & sum (SLIReal const * _p) throw () { s = 0.0L; while (_p) s += _p++->s; return * this; }
      virtual SLIReal & pro (SLIReal const * _p) throw () { s = 1.0L; while (_p) s *= _p++->s; return * this; }
      virtual SLIReal & neg (void) throw () { s =       -s; return * this; }
      virtual SLIReal & inv (void) throw () { s = 1.0L / s; return * this; }
      virtual SLIReal & sqr (void) throw () { s = sqrt (s); return * this; }

      /* input / output */
      friend std::ostream & operator << (std::ostream & _o, SLIReal const & _r) { _r.printOn  (_o); return _o; }
      friend std::istream & operator >> (std::istream & _i, SLIReal       & _r) { _r.readFrom (_i); return _i; }
  };



///////////////////////////////////////////////////////////////////////////////////////////////////
//  Metodos de calculo  ///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

template <class T>
class Method
  {
    protected:
      unsigned long long iter;
      T current;

    public:
      Method (void) throw () : iter (0), current ()  { return; }

      virtual ~Method () throw () { return; }

      virtual T nextIter (void) throw () { iter++; return getValue (); }
      virtual T getValue (void) throw () { return current; }

      virtual unsigned long long getIter (void) throw () { return iter; }
  };


template <class T>
class Gregory : public Method <T>
  {
    public:
      Gregory (void) throw () : Method <T> () { return; }

      virtual ~Gregory () throw () { return; }

      virtual T nextIter () throw ()
        {
          T * n_1 = this->current.create (1.0);
          T * n_2 = this->current.create (this->iter);
          T * n_3 = this->current.create (2.0);
          T * n_4 = this->current.create (this->iter % 2 ? -1.0 : 1.0);

          n_3->mul (* n_2); // 1
          n_3->add (* n_1); // 2
          n_4->div (* n_3); // 3

          this->current.add (* n_4);

          delete n_4;
          delete n_3;
          delete n_2;
          delete n_1;

          return Method <T>::nextIter ();
        }

      virtual T getValue (void) throw ()
        {
          T * n_1 = this->current.clone ();
          T * n_2 = this->current.create (4.0);

          n_1->mul (* n_2);

          delete n_2;

          return * n_1;
        }
  };


template <class T>
class Machin1 : public Method <T>
  {
    protected:
      T current2;

    public:
      Machin1 (void) throw () : Method <T> () { return; }

      virtual ~Machin1 () throw () { return; }

      virtual T nextIter () throw ()
        {
          T * n_1 = this->current.create (this->iter);
          T * n_2 = this->current.create (2.0);
          T * n_3 = this->current.create (1.0);
          T * n_4 = this->current.create (1.0 /   5.0); /// CAMBIAR
          T * n_5 = this->current.create (1.0 / 239.0); /// CAMBIAR

          n_2->mul (* n_1);
          n_2->add (* n_3);
          n_4->pow (* n_2);
          n_5->pow (* n_2);
          if (this->iter % 2) n_2->neg ();
          n_4->div (* n_2);
          n_5->div (* n_2);

          this->current.add (n_4);
          this->current2.add (n_5);

          delete n_5;
          delete n_4;
          delete n_3;
          delete n_2;
          delete n_1;

          return Method <T>::nextIter ();
        }

      virtual T getValue (void) throw ()
        {
          T * n_1 = this->current.clone  ();
          T * n_2 = this->current2.clone ();
          T * n_3 = this->current.create (4.0);

          n_1->mul (* n_3);
          n_1->sub (* n_2);
          n_1->mul (* n_3);

          delete n_3;
          delete n_2;

          return * n_1;
        }
  };


template <class T>
class Machin2 : public Method <T>
  {
    public:
      Machin2 (void) throw () : Method <T> () { return; }

      virtual ~Machin2 () throw () { return; }

      virtual T nextIter () throw ()
        {
          T * n_1 = this->current.create (this->iter);
          T * n_2 = this->current.create (2.0);
          T * n_3 = this->current.create (1.0);
          T * n_4 = this->current.create (1.0 /   5.0); /// CAMBIAR
          T * n_5 = this->current.create (1.0 / 239.0); /// CAMBIAR
          T * n_6 = this->current.create (4.0);

          n_2->mul (* n_1);  // 1
          n_2->add (* n_3);  // 2
          n_4->pow (* n_2);  // 3
          n_5->pow (* n_2);  // 4
          if (this->iter % 2) n_2->neg ();
          n_6->mul (* n_4);  // 5
          n_6->sub (* n_5);  // 6
          n_6->dib (* n_2);  // 7

          this->current.add (n_6);

          delete n_6;
          delete n_5;
          delete n_4;
          delete n_3;
          delete n_2;
          delete n_1;

          return Method <T>::nextIter ();
        }

      virtual T getValue (void) throw ()
        {
          T * n_1 = this->current.clone  ();
          T * n_2 = this->current.create (4.0);

          n_1->mul (* n_2);

          delete n_2;

          return * n_1;
        }
  };


template <class T>
class Ramanujan : public Method <T>
  {
    public:
      Ramanujan (void) throw () : Method <T> () { return; }

      virtual ~Ramanujan () throw () { return; }

      virtual T nextIter () throw () { Method <T>::nextIter (); return getValue (); }
      virtual T getValue (void) throw () { return this->current; }
  };



///////////////////////////////////////////////////////////////////////////////////////////////////
//  Criterios de corte  ///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*
template <class T>
class Criteria
  {
    protected:
      T epsilon;
      unsigned long long timeout;
      unsigned long long iterNum;
      Method <T> method;

    public:
      Criteria (const T & _e, unsigned long long _t, unsigned long long _i, Method <T> _m) throw ()
        : epsilon (_e), timeout (_t), iterNum (_i), method (_m)
        { return; }

      virtual ~Criteria () throw () { return; }

      T iterate ();
  };
*/


///////////////////////////////////////////////////////////////////////////////////////////////////
//  Main  /////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

int main (void)
  {
    /* inicializacion de gmp */
    mpfr::mpreal::set_default_prec (200);
    /* inicializacion de LongDouble */
    LongDouble::LongDouble_prec = 64;

    Gregory <LongDouble> G; G.nextIter (); G.nextIter ();
    std::cout << G.getValue () << std::endl;
    std::cout << G.getIter () << std::endl;

    Real * r = new SLIReal ();
    SLIReal s = SLIReal (1.0);
    dynamic_cast <SLIReal *> (r)->add (s);
    std::cout << * r << std::endl;

    std::cout << "Hello world!" << std::endl;
    return 0;
  }
